#ifndef ECATMANAGER_H
#define ECATMANAGER_H

#include <QObject>
#include <QDebug>
#include <QMutex>
#include <QMutexLocker>
#include <QReadWriteLock>
#include <QReadLocker>
#include <QWriteLocker>

#include "ecat_pdo_map.h"
#include "ecat_slave_define.h"
#include "ecat_cmd_define.h"

#define ECAT EcatManager::get_instance()

/**
 * @brief The EcatManager class
 * 提供Ecat从站的直接操作接口，包括：
 * 连接、断开连接;
 * 读写数据、同步总线;
 * 驱动器状态的读取
 * 驱动器控制的设置
 * TODO:
 * IO板接口
 */
class EcatManager : public QObject
{
    Q_OBJECT
private:
    explicit EcatManager(QObject *parent = nullptr);
    EcatManager(const EcatManager&) = delete ;
    EcatManager& operator=(const EcatManager&) = delete ;

public:
    ~EcatManager();
    static EcatManager* get_instance(); // 单例模式，获取唯一的EcatManager实例
    using servo_ctrl_t = EC_Ctrl_t;
    using servo_stat_t = EC_Stat_t;

    // 初始化各从站
    bool init_slaves(const QString& ifname);

    // 多次尝试初始化从站，直到完全成功或达到次数为止
    bool try_init_slaves(const QString &ifname, int retry_times = 10);

    // 读写总线，同步数据
    void sync();
    void read();
    void write();

    // 断开连接
    void exit_master();

    /* 状态获取（互斥锁保护） */

    // general state methods
    // 返回一个驱动器状态结构体的拷贝
    servo_stat_t get_servo_state(int slave_num);
    // 采用C风格，预分配结构体内存，传入可变引用赋值，不采用指针防止空指针错误
    void get_servo_state(int slave_num, servo_stat_t& state_ref);

    // specific state methods
    bool servo_state_fault(int slave_num); // 获取驱动器是否错误
    bool servo_state_switch_on_diabled(int slave_num); // 驱动器状态是否为switch on disabled
    bool servo_state_ready_to_switch_on(int slave_num); // 驱动器状态是否为ready to switch on
    bool servo_state_switched_on(int slave_num); // 驱动器状态是否为switched on
    bool servo_state_operation_enabled(int slave_num); // 驱动器状态是否为operation enabled
    position_actual_value_t servo_state_actual_pos(int slave_num); // 获取驱动器实际pos

    bool servo_state_has_fault(); // 是否有驱动器错误
    bool servo_state_all_operation_enabled(); // 是否所有驱动器都operation enabled
    // ADD MORE INTERFACE HERE...


    /* 控制设置（互斥锁保护） */

    // general set ctrl methods
    // 传入一个驱动器控制结构体的常引用
    void set_servo_ctrl(int slave_num, const servo_ctrl_t& ctrl_ref);
    void set_servo_control_word(int slave_num, control_word_t control_word);
    void set_servo_operation_mode(int slave_num, operation_mode_t op_mode);
    void set_servo_target_position(int slave_num, target_position_t target_pos);

    // specific set ctrl methods
    void servo_set_shutdown(int slave_num); // 设置驱动器控制命令为shutdown
    void servo_set_switch_on(int slave_num); // 设置驱动器控制命令为switch on
    void servo_set_enable_operation(int slave_num); // 设置驱动器控制命令为enable operation
    void servo_set_disable_operation(int slave_num); // 设置驱动器控制命令为disable operation
    void servo_set_fault_reset(int slave_num); // 设置驱动器控制命令为fault reset

    void servo_init_position(int slave_num); // 根据驱动器位置来初始化target position
    // ADD MORE INTERFACE HERE...


private:
    // 等待从站为SAFE OP模式
    bool wait_slaves_to_safe_op_();
    // 配置从站为OPERATIONAL模式
    bool conf_slaves_to_operational_();
    // 配置驱动器的PDO映射
    void conf_servo_pdo_map_();
    // 配置从站默认opmode
    void conf_servo_default_op_();
    // 初始化驱动器位置
    void init_servo_position_(int slave_num);

private: // lock
    QMutex ctrl_lock_;
    QMutex stat_lock_;

    // use read write lock
    QReadWriteLock rd_wr_lock_;

private:
    QString ifname_;
    char io_map_[IOMAP_SIZE];
    int expected_wkc_{0};
    volatile int wkc_{0}; // actual wkc

private:
    // 默认驱动器是前SERVO_NUM个
    // 用于驱动器IO MAP的指针数组，下标与SOEM统一从1开始，定义为SERVO_NUM + 1长度
    // 不可访问下标为0的指针
    servo_ctrl_t *servo_ctrl_[SERVO_NUM + 1];
    servo_stat_t *servo_stat_[SERVO_NUM + 1];

signals:

};

// usage: EcatSyncer(EcatManager::get_instance());
// read at construction, and write command when destruction
// do not new this class, construct it on stack
class EcatSyncer : public QObject
{
    Q_OBJECT
public:
    EcatSyncer(EcatManager* ecat = ECAT->get_instance()) : _ecat(ecat) {
        Q_ASSERT(_ecat != nullptr);
        _ecat->read();
    }
    ~EcatSyncer() {
        Q_ASSERT(_ecat != nullptr);
        _ecat->write();
    }
private:
    EcatManager* _ecat = nullptr;
    Q_DISABLE_COPY(EcatSyncer);
    void* operator new(size_t) = delete;
};

#endif // ECATMANAGER_H
